package main

import (
	"./Configs"
	"./Server"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strings"
)

func main() {
	var port int
	var config string

	flag.IntVar(&port, "port", 80, "server port")
	flag.StringVar(&config, "config", "", "mapping config")

	flag.Parse()

	if config == "" {
		log.Fatal("not found config")
	}

	if fileExist(config) == false {
		log.Fatal("not found config file")
	}

	data, err := ioutil.ReadFile(config)
	if err != nil {
		log.Fatal(err)
	}

	var configInstance Configs.Config
	err = json.Unmarshal(data, &configInstance)
	if err != nil {
		log.Fatal(err)
	}

	auths := make(map[string]Server.IAuthInterface)

	if configInstance.AuthItems != nil {
		for name, configItem := range configInstance.AuthItems {
			auth_item := Server.GetAuthFactoryInstance().CreateAuthInstance(configItem.OAuthType)

			if auth_item == nil {
				continue
			}

			auth_item.InitWithConfig(configItem.Configs)
			auths[strings.ToLower(name)] = auth_item
			log.Println(name, configItem)
		}
	}

	for i := 0; i < len(configInstance.Upstreams); i++ {
		up := configInstance.Upstreams[i]
		u, err := url.Parse(up.Upstream)

		log.Printf("{%s} => {%s}\r\n", up.Application, up.Upstream)

		if err != nil {
			log.Fatal(err)
		}

		rp := httputil.NewSingleHostReverseProxy(u)

		http.HandleFunc(up.Application, func(writer http.ResponseWriter, request *http.Request) {
			o_path := request.URL.Path

			if up.UpHost != "" {
				request.Host = up.UpHost
			} else {
				request.Host = u.Host
			}

			if up.TrimApplication {
				request.URL.Path = strings.TrimPrefix(request.URL.Path, up.Application)
			}

			if up.IsAuth {
				auth_value := request.Header.Get("Authorization")
				if auth_value == "" {
					writeUnAuthorized(writer)
					return
				}

				sp_index := strings.Index(auth_value, " ")
				auth_type := auth_value[:sp_index]
				auth_token := auth_value[sp_index+1:]

				if auth_instance, ok := auths[strings.ToLower(auth_type)]; ok {
					err, headers := auth_instance.GetAuthInfo(auth_token, o_path)
					if err != nil {
						writeUnAuthorized(writer)
					} else {
						if headers != nil {
							for k, v := range headers {
								request.Header.Add(k, v)
							}
						}
						rp.ServeHTTP(writer, request)
					}
				} else {
					writeUnsupportedAuthType(writer)
				}
			} else {
				rp.ServeHTTP(writer, request)
			}
		})
	}

	log.Printf("http server start on :%d\r\n", port)
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	log.Println("finsh")
}

func writeUnsupportedAuthType(writer http.ResponseWriter) () {
	writer.Header().Add("Content-Type", "Application/json")
	writer.WriteHeader(http.StatusBadRequest)
	writer.Write([]byte("{\"status\":\"unsupported authorization\"}"))
}

func writeUnAuthorized(writer http.ResponseWriter) {
	writer.Header().Add("Content-Type", "Application/json")
	writer.WriteHeader(http.StatusUnauthorized)
	writer.Write([]byte("{\"status\":\"un-authorized\"}"))
}

func fileExist(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil || os.IsExist(err)
}
